<?php
/*
 * @Description    : 
 * @Author         : QianLong
 * @Date           : 2022-07-17 19:10:36
 * @LastEditors    : QianLong
 * @Site           : http://21ds.cn
 * @LastEditTime   : 2024-04-17 15:52:01
 */

namespace app\api\controller;

use think\facade\Validate;
use think\facade\Db;
use app\lib\Result;

class Logs extends Base
{
  /**
   * 列表
   * @Author Qianlong <87498106@qq.com>
   * @PersonSite http://www.21ds.cn/
   */
  public function list()
  {
    $rule = [
      'pageNo'  => 'number',
      'pageSize'  => 'number',
      'send_time_ranges|日志时间'  => 'require',
    ];
    $data = [
      'pageNo' => input('param.pageNo', 1, 'trim'),
      'title' => input('param.title', '', 'trim'),
      'lsi_id' => input('param.lsi_id', '', 'trim'),
      'jq_params' => input('param.jq_params', '', 'trim'),
      'jq_title' => input('param.jq_title', '', 'trim'),
      'send_time_ranges' => input('param.send_time_ranges', '', 'trim'),
      'pageSize' => input('param.pageSize', 15, 'trim'),
    ];
    $validate = Validate::rule($rule);
    $result   = $validate->check($data);
    if (!$result) {
      return Result::Error($validate->getError());
    }
    if (empty($data['lsi_id'])) {
      return Result::Success([], '站点必须选择');
    }
    $start_time = strtotime($data['send_time_ranges'][0]);
    $end_time = strtotime($data['send_time_ranges'][1]);
    if ($end_time - $start_time > 86400 * 90) {
      return Result::Error('时间跨度不能大于90天');
    }
    $siteInfo = Db::connect('mongodb')->name('log_site_list')->where('_id', $data['lsi_id'])->find();
    if (!$siteInfo) {
      return Result::Error('站点不存在或已删除');
    }
    // 构建查询条件
    $where = [];
    $where[] = ['lsi_id','=',$data['lsi_id']];
    $where[] = ['uid','=', $this->uid];
    if (!empty($data['title'])) {
      if (empty($siteInfo['search_params'])) {
        $where[] = ['_id', 'like', $data['title']];
      } else {
        $where[] = ['_id|' . $siteInfo['search_params'], 'like', $data['title']];
      }
    }
    if (!empty($data['jq_title']) && !empty($data['jq_params'])) {
        $where[] = [$data['jq_params'], 'like', $data['jq_title']];
    }
    $limit = ($data['pageNo'] - 1) * $data['pageSize'];
    $dataList = Db::connect('mongodb')->name('site_log')->where($where)->whereBetweenTime('create_time', $start_time, $end_time)->order('create_time', 'desc')->limit($limit, $data['pageSize'])->select()->toArray();
    $totalcount = Db::connect('mongodb')->name('site_log')->where($where)->whereBetweenTime('create_time', $start_time, $end_time)->count('_id');
    foreach ($dataList as &$vo) {
      $vo['create_time'] = date('Y-m-d H:i:s', $vo['create_time']);
      unset($vo['delete_time'], $vo['only_hour'], $vo['date']);
    }
    $returnData['data'] = $dataList;
    $returnData['totalCount'] = $totalcount;
    $returnData['pageNo'] = (int) $data['pageNo'];
    $returnData['pageSize'] = (int) $data['pageSize'];
    return Result::Success($returnData);
  }
  public function line_data()
  {
    $rule = [
      'send_time_ranges|日志时间'  => 'require',
      'lineField|统计参数'  => 'require',
      'lsi_id|站点'  => 'require',
    ];
    $data = [
      'lineField' => input('param.lineField', '', 'trim'),
      'title' => input('param.title', '', 'trim'),
      'lsi_id' => input('param.lsi_id', '', 'trim'),
      'jq_params' => input('param.jq_params', '', 'trim'),
      'jq_title' => input('param.jq_title', '', 'trim'),
      'send_time_ranges' => input('param.send_time_ranges', '', 'trim'),
    ];
    $validate = Validate::rule($rule);
    $result   = $validate->check($data);
    if (!$result) {
      return Result::Error($validate->getError());
    }
    $start_time = strtotime($data['send_time_ranges'][0]);
    $end_time = strtotime($data['send_time_ranges'][1]);
    if ($end_time - $start_time > 86400 * 90) {
      return Result::Error('时间跨度不能大于90天');
    }
    $siteInfo = Db::connect('mongodb')->name('log_site_list')->where('_id', $data['lsi_id'])->find();
    if (!$siteInfo) {
      return Result::Error('站点不存在或已删除');
    }
    // 构建查询条件
    $where = [];
    $where[] = ['lsi_id', '=', $data['lsi_id']];

    $whereM = [];
    $whereM['lsi_id'] = $data['lsi_id'];
    $whereM['uid'] = $this->uid;
    if (!empty($data['title'])) {
      if (empty($siteInfo['search_params'])) {
        $where[] = ['_id', 'like', $data['title']];
        $whereM['$or'] = [
          ['_id' => ['$regex' => $data['title']]],
        ];
      } else {
        $where[] = ['_id|' . $siteInfo['search_params'], 'like', $data['title']];
        $fieldStr = '_id|' . $siteInfo['search_params'];
        $fieldArray = explode('|', $fieldStr);
        foreach ($fieldArray as $field) {
          $whereM['$or'][] = [$field => ['$regex' => $data['title']]];
        }
      }
    }
    if (!empty($data['jq_title']) && !empty($data['jq_params'])) {
        $where[] = [$data['jq_params'], 'like', $data['jq_title']];
        $whereM[$data['jq_params']] = ['$regex' => $data['jq_title']];
    }
    $totalcount = Db::connect('mongodb')->name('site_log')->where($where)->whereBetweenTime('create_time', $start_time, $end_time)->count('_id');
    $where['create_time'] = ['$gte' => $start_time, '$lte' => $end_time];
    $newData = [];
    $newData2 = [];
    $hasData = mongodb()->selectCollection('site_log_' . $this->uid)->aggregate([
      ['$match' => $whereM],
      ['$group' => ['_id' => '$' . $data['lineField'], 'count' => ['$sum' => 1]]],
      ['$sort' => ['count' => -1]]
    ]);
    $cycle_type = (date('Y-m-d', $start_time) == date('Y-m-d', $end_time)) ? 'hour' : 'day';
    $groupField = $cycle_type == 'hour' ? 'only_hour' : 'date';
    $hasData2 = mongodb()->selectCollection('site_log_' . $this->uid)->aggregate([
      ['$match' => $whereM],
      ['$group' => ['_id' => '$' . $groupField, 'count' => ['$sum' => 1]]],
      ['$sort' => ['count' => -1]]
    ]);
    foreach ($hasData as $document) {
      $newData[] = (array)$document;
    }
    foreach ($hasData2 as $document) {
      $newData2[] = (array)$document;
    }
    foreach ($newData as $key => $vo) {
      $newData[$key]['rate'] = round($vo['count'] / $totalcount * 100, 3);
    }
    unset($where['create_time']);
    $start_date = strtotime(date('Y-m-d', $start_time));
    $end_date = strtotime(date('Y-m-d', $end_time));
    if ($cycle_type == 'hour') {
      $cycle_num = 24;
    } else {
      $cycle_num = ($end_date - $start_date) / 86400;
    }
    $fieldCount = [];
    foreach ($newData2 as $key => $vo) {
      $fieldCount[$vo['_id']] = $vo['count'];
    }
    $chartData = [];
    for ($i = 0; $i < $cycle_num; $i++) {
      if ($cycle_type == 'hour') {
        $nowTime = $start_date + $i * 3600;
        $charItem['date'] = date('H', $nowTime) . ':00';
      } else {
        $nowTime = $start_date + $i * 86400;
        $charItem['date'] = date('Y-m-d', $nowTime);
      }
      $charItem['value'] = isset($fieldCount[$charItem['date']]) ? $fieldCount[$charItem['date']] : 0;
      $charItem['category'] = '新增日志数';
      $chartData[] = $charItem;
    }
    if ($cycle_type == 'day') {
      $charItem['date'] = date('Y-m-d', $end_date);
      $charItem['value'] = isset($fieldCount[$charItem['date']]) ? $fieldCount[$charItem['date']] : 0;
      $charItem['category'] = '新增日志数';
      $chartData[] = $charItem;
    }
    $return['chart2'] = $newData;
    $return['chart'] = $chartData;
    return Result::Success($return);
  }
  public function site_list()
  {
    $rule = [
      'pageNo'  => 'number',
      'pageSize'  => 'number',
    ];
    $data = [
      'pageNo' => input('param.pageNo', 1, 'trim'),
      'title' => input('param.title', '', 'trim'),
      'pageSize' => input('param.pageSize', 15, 'trim'),
    ];
    $validate = Validate::rule($rule);
    $result   = $validate->check($data);
    if (!$result) {
      return Result::Error($validate->getError());
    }
    // 构建查询条件
    $where = [];

    if (!empty($data['title'])) {
      $where[] = ['title|_id', 'like', $data['title']];
    }
    $limit = ($data['pageNo'] - 1) * $data['pageSize'];
    $serverList = Db::connect('mongodb')->name('push_server_list')->field('_id,title')->select()->toArray();
    $serverListArr = [];
    foreach ($serverList as &$vo) {
      $serverListArr[$vo['_id']] = $vo['title'];
    }
    $dataList = Db::connect('mongodb')->name('log_site_list')->where($where)->order('create_time', 'desc')->limit($limit, $data['pageSize'])->select()->toArray();
    $totalcount = Db::connect('mongodb')->name('log_site_list')->where($where)->count('_id');
    foreach ($dataList as &$vo) {
      $vo['create_time'] = date('Y-m-d H:i:s', $vo['create_time']);
      $vo['push_server_title'] = (isset($vo['notice_open']) && $vo['notice_open'] == 1) ? (isset($serverListArr[$vo['ps_id']]) ? $serverListArr[$vo['ps_id']] : '未知或不存在') : '';
      if (isset($vo['search_params']) && !empty($vo['search_params'])) {
        $vo['search_params'] = explode('|', $vo['search_params']);
      }
      if (isset($vo['notice_params']) && !empty($vo['notice_params'])) {
        $vo['notice_params'] = explode('|', $vo['notice_params']);
      }
    }
    $returnData['data'] = $dataList;
    $returnData['totalCount'] = $totalcount;
    $returnData['pageNo'] = (int) $data['pageNo'];
    $returnData['pageSize'] = (int) $data['pageSize'];
    return Result::Success($returnData);
  }
  public function all_site_list()
  {
    // 构建查询条件
    // $where['status'] = 1;
    $where['uid'] = $this->uid;
    $title = input('param.title', '', 'trim');
    if (!empty($title)) {
      $dataList = Db::connect('mongodb')->name('log_site_list')->field('_id,title,columns,searchTitle,fieldsName,browserParams,lineFields,status,searchParamsOptions')->where($where)->whereLike('title|_id', $title)->order('create_time', 'desc')->limit(16)->select()->toArray();
    } else {
      $dataList = Db::connect('mongodb')->name('log_site_list')->field('_id,title,columns,searchTitle,fieldsName,browserParams,lineFields,status,searchParamsOptions')->where($where)->order('create_time', 'desc')->limit(16)->select()->toArray();
    }
    foreach ($dataList as $key => $vo) {
      $dataList[$key]['title'] = ($vo['status'] != 1)?$vo['title'].'(未启用)':$vo['title'];
    }
    $buildData = [];
    $operateBtn['title'] = '操作';
    $operateBtn['width'] = '150px';
    $operateBtn['slots']['customRender'] = 'action';
    $idField = ['title' => 'ID', 'dataIndex' => '_id', 'width' => '239px'];
    $buildData['list'] = [];
    $buildData['columns'] = [];
    $buildData['searchTitle'] = [];
    $buildData['fieldsName'] = [];
    $buildData['lineFields'] = [];
    $buildData['browserParams'] = [];
    $buildData['searchParamsOptions'] = [];
    foreach ($dataList as $key => $vo) {
      $buildData['list'][] = ['label' => $vo['title'], 'value' => $vo['_id']];
      $vo['columns'][] = $operateBtn;
      array_unshift($vo['columns'], $idField);
      $buildData['columns'][$vo['_id']] = $vo['columns'];
      $buildData['searchTitle'][$vo['_id']] = $vo['searchTitle'];
      $buildData['fieldsName'][$vo['_id']] = $vo['fieldsName'];
      $buildData['lineFields'][$vo['_id']] = !isset($vo['lineFields']) ? [] : $vo['lineFields'];
      if (isset($vo['browserParams']) && !empty($vo['browserParams'])) {
        $buildData['browserParams'][$vo['_id']] = $vo['browserParams'];
      }
      if (isset($vo['searchParamsOptions']) && !empty($vo['searchParamsOptions'])) {
        $buildData['searchParamsOptions'][$vo['_id']] = $vo['searchParamsOptions'];
      }
    }
    return Result::Success($buildData);
  }
  public function editSiteInfo()
  {
    if (request()->isPost()) {
      $rule = [
        'title|标题'  => 'require|max:128',
        'status|状态'  => 'require',
        'save_day|日志存储天数'  => 'require|number',
        'log_params|日志参数'  => 'require',
        'search_params|搜索参数'  => 'require',
        'server_secret|SecretKey'  => 'require',
      ];
      $data = [
        'id' => input('param._id', '', 'trim'),
        'title' => input('param.title', '', 'trim'),
        'status' => input('param.status', '', 'trim'),
        'save_day' => input('param.save_day', '', 'trim'),
        'search_params' => input('param.search_params', '', 'trim'),
        'searchParamsOptions' => input('param.searchParamsOptions', '', 'trim'),
        'log_params' => input('param.log_params', '', 'trim'),
        'server_secret' => input('param.server_secret', '', 'trim'),
        'desc' => input('param.desc', '', 'trim'),
        'ps_id' => input('param.ps_id', '', 'trim'),
        'notice_open' => input('param.notice_open', '', 'trim'),
        'notice_params' => input('param.notice_params', '', 'trim'),
        'browser_label_params' => input('param.browser_label_params', '', 'trim'),
        'notice_params_ignore' => input('param.notice_params_ignore', '', 'trim'),
      ];
      $validate = Validate::rule($rule);
      $result   = $validate->check($data);
      if (!$result) {
        return json(['code' => -1, 'data' => '', 'msg' => $validate->getError()]);
      }
      $limitCount = Db::connect('mongodb')->name('log_site_list')->count();
      $this->security::checkVersionLimit($this->user['version'], 'log_site_num', '日志站点数量', $limitCount);
      $versionLimit = $this->redisCache->getVersionInfo($this->user['version']);
      if ($data['save_day'] > $versionLimit['log_site_save_day']) {
        return Result::Error('日志存储天数不得大于' . $versionLimit['log_site_save_day']);
      }
      $data['browserParams'] = [];
      $data['searchParamsOptions'] = !empty($data['searchParamsOptions'])?json_decode($data['searchParamsOptions'],true):[];
      if (isset($data['browser_label_params']) && !empty($data['browser_label_params'])) {
        $browser_label_params = trim($data['browser_label_params'], '|');
        if (!empty($browser_label_params)) {
          $browserParamsArr = explode("\n", $browser_label_params);
          foreach ($browserParamsArr as $vo) {
            $browserVoArray = explode('=', $vo);
            if (!empty($browserVoArray) && count($browserVoArray) >= 2) {
              $fieldVoArray = explode('|', $browserVoArray[1]);
              if (!empty($fieldVoArray)) {
                $data['browserParams'][$browserVoArray[0]] = $fieldVoArray;
              }
            }
          }
        }
      }
      if (!empty($data['notice_params'])) {
        $data['notice_params'] = implode('|', $data['notice_params']);
      }
      if (!empty($data['search_params'])) {
        $data['search_params'] = implode('|', $data['search_params']);
      }
      $data['search_params'] = trim($data['search_params'], '|');
      $data['log_params'] = trim($data['log_params'], '|');
      $data['status'] = intval($data['status']);
      $data['notice_open'] = intval($data['notice_open']);
      $this->redis->del('dever:log:site:' . $data['id'], 'full');
      $paramsArr = explode("\n", $data['log_params']);
      $columns = [];
      $fields = [];
      $fieldsName = [];
      $lenOutFields = [];
      $lineFields = [];
      foreach ($paramsArr as $vo) {
        $voArray = explode('|', $vo);
        if (!empty($voArray) && count($voArray) >= 2) {
          $fields[] = $voArray[1];
          if (strlen($voArray[1]) > 18) {
            $lenOutFields[] = $voArray[1];
          }
          $fieldsName[$voArray[1]] = $voArray[0];
          if (!in_array($voArray[1], ['_id', 'request_id'])) {
            $lineFields[] = ['label' => $voArray[0], 'value' => $voArray[1]];
          }
          if (isset($voArray[2]) && $voArray[2] == 1) {
            if (isset($voArray[3]) && is_numeric($voArray[3])) {
              $columns[] = ['title' => $voArray[0], 'dataIndex' => $voArray[1], 'width' => $voArray[3] . 'px', 'ellipsis' => true];
            } else {
              $columns[] = ['title' => $voArray[0], 'dataIndex' => $voArray[1], 'ellipsis' => true];
            }
          }
        }
      }
      if (empty($fields)) {
        return json(['code' => -1, 'data' => '', 'msg' => '日志参数不正确']);
      }
      if (!empty($lenOutFields)) {
        return Result::Error(implode(',', $lenOutFields) . ' 字段长度超过18位，请修改');
      }
      if (count($fields) > 20) {
        return Result::Error('字段数量不得超过20个，请修改');
      }
      if ($this->fieldCheck($fields) === false) {
        return json(['code' => -1, 'data' => '', 'msg' => '日志参数中包含系统保留参数']);
      }
      $searchArray = explode('|', $data['search_params']);
      if (!empty($searchArray)) {
        if ($this->fieldCheck($searchArray) === false) {
          return json(['code' => -1, 'data' => '', 'msg' => '搜索参数中包含系统保留参数']);
        }
      }
      $noticeArray = explode('|', $data['notice_params']);
      if (!empty($noticeArray)) {
        if ($this->fieldCheck($noticeArray) === false) {
          return json(['code' => -1, 'data' => '', 'msg' => '通知包含参数中含有系统保留参数']);
        }
      }
      $data['searchTitle'] = '';
      $unexistsKey = '';
      foreach ($searchArray as $key) {
        $fieldsName[$key] = !isset($fieldsName[$key]) ? 'none' : $fieldsName[$key];
        $data['searchTitle'] .= $fieldsName[$key] . '/';
        if (!in_array($key, $fields)) {
          $unexistsKey .= $key . '/';
        }
      }
      $unexistsKey = rtrim($unexistsKey, '/');
      if (!empty($unexistsKey)) {
        return json(['code' => -1, 'data' => '', 'msg' => '搜索参数超出日志参数范围：' . $unexistsKey]);
      }
      $data['searchTitle'] = rtrim($data['searchTitle'], '/');
      $data['columns'] = $columns;
      $data['fields'] = $fields;
      $data['fieldsName'] = $fieldsName;
      $data['lineFields'] = $lineFields;
      if (!empty($data['id'])) {
        $status = Db::connect('mongodb')->name('log_site_list')->where('_id', $data['id'])->update($data);
        $tag = '编辑';
      } else {
        unset($data['id']);
        $data['create_time'] = time();
        $data['uid'] = $this->uid;
        $data['username'] = $this->username;
        $data['_id'] = createId('LSI');
        $status = Db::connect('mongodb')->name('log_site_list')->insert($data);
        $tag = '新增';
      }
      if ($status > 0) {
        return json(['code' => 200, 'data' => '', 'msg' => $tag . '成功']);
      } else {
        return json(['code' => -1, 'data' => '', 'msg' => $tag . '失败，请重试']);
      }
    }
  }
  private function fieldCheck($field)
  {
    $intersection = array_intersect(['_id', 'uid', 'username', 'create_time'], $field);
    if (empty($intersection)) {
      return true;
    }
    return false;
  }
  public function del_site()
  {
    $rule = [
      '_id'  => 'require',
    ];
    $data = [
      '_id' => input('param.id', '', 'trim'),
    ];
    $validate = Validate::rule($rule);
    $result   = $validate->check($data);
    if (!$result) {
      return json(['code' => -1, 'data' => '', 'msg' => $validate->getError()]);
    }
    $infoData = Db::connect('mongodb')->name('log_site_list')->field('_id')->where('_id', $data['_id'])->find();
    if (empty($infoData)) {
      return json(['code' => -1, 'data' => '', 'msg' => '内容不存在或网络异常，请稍后重试']);
    }
    $status = Db::connect('mongodb')->name('log_site_list')->where('_id', $data['_id'])->delete();
    Db::connect('mongodb')->name('site_log')->where('lsi_id', $data['_id'])->delete();
    if ($status > 0) {
      return json(['code' => 200, 'data' => '', 'msg' => '删除成功']);
    } else {
      return json(['code' => -1, 'data' => '', 'msg' => '删除失败，请稍后重试']);
    }
  }
  public function del_site_batch()
  {
    $rule = [
      'id'  => 'require',
    ];
    $data = [
      'id' => input('param.ids/a', '', 'trim'),
    ];
    $validate = Validate::rule($rule);
    $result   = $validate->check($data);
    if (!$result) {
      return json(['code' => -1, 'data' => '', 'msg' => $validate->getError()]);
    }
    $status = Db::connect('mongodb')->name('log_site_list')->whereIn('_id', $data['id'])->delete();
    Db::connect('mongodb')->name('site_log')->whereIn('lsi_id', $data['id'])->delete();
    if ($status > 0) {
      return json(['code' => 200, 'data' => '', 'msg' => '删除成功']);
    } else {
      return json(['code' => -1, 'data' => '', 'msg' => '删除失败，请稍后重试']);
    }
  }
  public function del_log()
  {
    $rule = [
      '_id'  => 'require',
    ];
    $data = [
      '_id' => input('param.id', '', 'trim'),
    ];
    $validate = Validate::rule($rule);
    $result   = $validate->check($data);
    if (!$result) {
      return json(['code' => -1, 'data' => '', 'msg' => $validate->getError()]);
    }
    $status = Db::connect('mongodb')->name('site_log')->where('_id', $data['_id'])->delete();
    if ($status > 0) {
      return json(['code' => 200, 'data' => '', 'msg' => '删除成功']);
    } else {
      return json(['code' => -1, 'data' => '', 'msg' => '删除失败，请稍后重试']);
    }
  }
  public function del_log_all()
  {
    $rule = [
      'lsi_id'  => 'require',
      'type|清空类型'  => 'require',
    ];
    $data = [
      'lsi_id' => input('param.lsi_id', '', 'trim'),
      'type' => input('param.type', '', 'trim'),
      'send_time_ranges' => input('param.send_time_ranges/a', '', 'trim'),
    ];
    $validate = Validate::rule($rule);
    $result   = $validate->check($data);
    if (!$result) {
      return json(['code' => -1, 'data' => '', 'msg' => $validate->getError()]);
    }
    if ($data['type'] == 'all') {
      $status = Db::connect('mongodb')->name('site_log')->where('lsi_id', $data['lsi_id'])->delete();
    } else {
      $start_time = strtotime($data['send_time_ranges'][0]);
      $end_time = strtotime($data['send_time_ranges'][1]);
      if ($end_time - $start_time > 86400 * 90) {
        return Result::Error('时间跨度不能大于90天');
      }
      $status = Db::connect('mongodb')->name('site_log')->where('lsi_id', $data['lsi_id'])->whereBetweenTime('create_time', $start_time, $end_time)->delete();
    }
    if ($status > 0) {
      return json(['code' => 200, 'data' => '', 'msg' => '删除成功']);
    } else {
      return json(['code' => -1, 'data' => '', 'msg' => '删除失败，请稍后重试']);
    }
  }
  public function del_log_batch()
  {
    $rule = [
      'id'  => 'require',
    ];
    $data = [
      'id' => input('param.ids/a', '', 'trim'),
    ];
    $validate = Validate::rule($rule);
    $result   = $validate->check($data);
    if (!$result) {
      return json(['code' => -1, 'data' => '', 'msg' => $validate->getError()]);
    }
    $status = Db::connect('mongodb')->name('site_log')->whereIn('_id', $data['id'])->delete();
    if ($status > 0) {
      return json(['code' => 200, 'data' => '', 'msg' => '删除成功']);
    } else {
      return json(['code' => -1, 'data' => '', 'msg' => '删除失败，请稍后重试']);
    }
  }
}
